#pragma once
#include "coro.h"
#include "loop.h"
#include "stm32f1xx_hal.h"
#include <coroutine>
#include <queue>
#include <unordered_map>


class Timer {

  struct TimerTask {
    uint32_t target_cnt;
    std::coroutine_handle<> coro;
    bool operator<(const TimerTask &other) const {
      return target_cnt > other.target_cnt;
    }
  };

public:
  Timer(TIM_HandleTypeDef *timer) : m_timer(timer) {}
  Timer(Timer &&other) : m_timer(other.m_timer) { other.m_timer = nullptr; }

  Task<void> wait(uint32_t ms) {
    auto coro = co_await GetCoroutine<Promise<void>>();
    TimerTask task{ms, std::move(coro)};
    m_tasks.push(task);
  }

  bool empty() const noexcept { return m_tasks.empty(); }

  const TimerTask &top() const { return m_tasks.top(); }

  TimerTask pop() {
    auto re = std::move(m_tasks.top());
    m_tasks.pop();
    return re;
  }

private:
  TIM_HandleTypeDef *m_timer;

  std::priority_queue<TimerTask> m_tasks;
};

class TimerManager {
public:
  static TimerManager &get() {
    static TimerManager instance;
    return instance;
  }
  static Timer &timer(TIM_HandleTypeDef *timer) {
    if (!m_timers.contains(timer)) {
      m_timers.emplace(timer, timer);
    }
    return m_timers.at(timer);
  }

private:
  static std::unordered_map<TIM_HandleTypeDef *, Timer> m_timers;
  TimerManager() {}
};

inline void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
  auto &timer = TimerManager::timer(htim);
  while (!timer.empty() && timer.top().target_cnt <= htim->Instance->CNT) {
    Loop::get().enqueue(std::move(timer.pop().coro));
  }
}
